package miniproj.services.data;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;

import miniproj.models.building.Building;
import miniproj.models.groups.Group;
import miniproj.models.groups.Promotion;
import miniproj.models.groups.Teaching;
import miniproj.models.people.Student;
import miniproj.models.people.Teacher;
import miniproj.models.reservation.Date;
import miniproj.models.reservation.Reservation;
import miniproj.models.reservation.Schedule;
import miniproj.models.room.Amphi;
import miniproj.models.room.ClassRoom;
import miniproj.models.room.MeetingRoom;
import miniproj.models.room.Room;
import miniproj.models.room.RoomId;
import miniproj.models.room.TDRoom;
import miniproj.models.room.TPRoom;

/**
 * This class is able to load data using the personalized way
 * 
 * @author William Tassoux
 * 
 */
public class SpecialDataLoader implements DataLoader {

	private String folderPath;

	/**
	 * Instantiates a new special data loader.
	 * 
	 * @param folderPath
	 *            the folder path
	 */
	public SpecialDataLoader(String folderPath) {
		this.folderPath = folderPath;
	}

	/**
	 * Load the DataContext using the special data format.
	 * 
	 * @param dataContextFileName
	 *            the data context file name
	 * @return the data context
	 */
	@Override
	public DataContext getDataContext(String dataContextFileName) {
		String fileFullPath = this.folderPath + "/" + dataContextFileName;
		DataContext data;

		data = DataContext.getDataContext();
		this.getContent(data, fileFullPath);

		return data;
	}

	private void getContent(DataContext data, String fileFullPath) {
		File dataFile = new File(fileFullPath);

		if (!dataFile.exists())
			System.err.println("Data file " + fileFullPath + " not found !");

		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(dataFile));

			String buffer;
			String[] splittedBuffer;
			while ((buffer = br.readLine()) != null) {
				splittedBuffer = buffer.split(";");
				determineContent(data, splittedBuffer);
			}
			br.close();
		} catch (IOException e) {
			System.err.println("Input/Output Exception while saving object : "
					+ e.getMessage());
		} catch (Exception e) {
			System.err.println("Invalid data in " + fileFullPath
					+ ". Not all data was loaded: " + e.getMessage());
		}
	}

	private void determineContent(DataContext data, String[] splittedBuffer)
			throws Exception {
		if (splittedBuffer.length == 0)
			throw new Exception("Error: The line was empty");
		else if (splittedBuffer[0].equals("Teacher")) {
			loadTeacher(data, splittedBuffer);
		} else if (splittedBuffer[0].equals("Student")) {
			loadStudent(data, splittedBuffer);
		} else if (splittedBuffer[0].equals("Group")) {
			loadGroup(data, splittedBuffer);
		} else if (splittedBuffer[0].equals("Building")) {
			loadBuilding(data, splittedBuffer);
		} else if (splittedBuffer[0].equals("Room")) {
			loadRoom(data, splittedBuffer);
		} else if (splittedBuffer[0].equals("Promotion")) {
			loadPromotion(data, splittedBuffer);
		} else if (splittedBuffer[0].equals("Teaching")) {
			loadTeaching(data, splittedBuffer);
		} else if (splittedBuffer[0].equals("Reservation")) {
			loadReservation(data, splittedBuffer);
		} else {
			throw new Exception("Error: Attempted to load an invalid object");
		}
	}

	private void loadReservation(DataContext data, String[] splittedBuffer)
			throws Exception {
		Reservation reservation;
		Teacher teacher;
		LinkedList<Group> groups;
		Group group;
		Teaching teaching;
		RoomId roomId;
		Room room;
		Building building;
		Date date;
		long timeMillis = 0;
		Schedule schedule;

		if (splittedBuffer.length < 10)
			throw new Exception("Error: Invalid Reservation");

		teacher = teacherLookup(data, splittedBuffer[1], splittedBuffer[2]);
		if (teacher == null)
			throw new Exception(
					"Error: The teacher who made the reservation wasn't found");
		building = roomBuildingLookup(data, splittedBuffer[3]);
		if (building == null)
			throw new Exception(
					"Error: The building in which the reserved room is was not found");

		roomId = new RoomId(building, Integer.parseInt(splittedBuffer[4]),
				Integer.parseInt(splittedBuffer[5]));
		room = roomLookup(data, roomId);
		if (room == null)
			throw new Exception("Error: The reserved room was not found");

		timeMillis = Long.parseLong(splittedBuffer[6]);
		schedule = Schedule.valueOf(splittedBuffer[7]);
		date = new Date(timeMillis, schedule);
		teaching = teachingLookup(data, splittedBuffer[8]);
		groups = new LinkedList<Group>();
		for (int i = 9; i < splittedBuffer.length; ++i) {
			group = groupLookup(data, splittedBuffer[i]);

			if (group == null)
				throw new Exception(
						"Error: The group for which the reservation was made was not found");
			else if (groups.contains(group))
				throw new Exception(
						"Error: Duplicate group for the reservation");
			groups.add(group);
		}

		reservation = new Reservation(teacher, date, room, teaching, groups);
		data.getReservations().add(reservation);
	}

	private Teaching teachingLookup(DataContext data, String teachingName) {

		for (Teaching teaching : data.getTeachings()) {
			if (teaching.getName().toUpperCase()
					.equals(teachingName.toUpperCase())) {
				return teaching;
			}
		}
		return null;
	}

	private Room roomLookup(DataContext data, RoomId roomId) {

		for (Room room : data.getRooms()) {
			if (room.getId().equals(roomId))
				return room;
		}
		return null;
	}

	private void loadTeaching(DataContext data, String[] splittedBuffer)
			throws Exception {

		Teacher headTeacher;
		Teacher currentTeacher;
		Group currentGroup;
		LinkedList<Teacher> teachers;
		LinkedList<Group> groups;
		int nbTeachers;
		int nbGroups;

		teachers = new LinkedList<Teacher>();
		groups = new LinkedList<Group>();
		if (splittedBuffer.length < 6)
			throw new Exception("Error: Invalid teaching");

		nbTeachers = Integer.parseInt(splittedBuffer[4]);
		if (nbTeachers < 0)
			throw new Exception(
					"Error: The teaching can't have a negative number of teachers");
		nbGroups = splittedBuffer.length - 3 - (nbTeachers + 1) * 2;
		if (nbGroups < 0)
			throw new Exception("Error: Invalid teaching");

		headTeacher = teacherLookup(data, splittedBuffer[2], splittedBuffer[3]);
		if (headTeacher == null)
			throw new Exception("Error: The head teacher cannot be found");

		for (int i = 0; i < nbTeachers; ++i) {
			currentTeacher = teacherLookup(data, splittedBuffer[i + 5],
					splittedBuffer[i + 6]);
			if (currentTeacher == null)
				throw new Exception("Error: A teacher specified was not found");
			if (teachers.contains(currentTeacher))
				throw new Exception("Error: Duplicate teacher");
			teachers.add(currentTeacher);
		}

		for (int i = 0; i < nbGroups; ++i) {
			currentGroup = groupLookup(data, splittedBuffer[i + 5 + nbTeachers
					* 2]);
			if (currentGroup == null) {
				throw new Exception("Error: A group specified was not found");
			}
			if (groups.contains(currentGroup)) {
				throw new Exception("Error: Duplicate group");
			}
			groups.add(currentGroup);
		}

		Teaching teaching = new Teaching(headTeacher, teachers, groups,
				splittedBuffer[1]);
		data.getTeachings().add(teaching);
	}

	private void loadPromotion(DataContext data, String[] splittedBuffer)
			throws Exception {
		LinkedList<Group> groups;
		Group currentGroup;
		Teacher headTeacher;
		Promotion promo;

		groups = new LinkedList<Group>();
		if (splittedBuffer.length < 4) {
			throw new Exception("Error: Invalid promotion");
		}
		headTeacher = teacherLookup(data, splittedBuffer[2], splittedBuffer[3]);
		if (headTeacher == null)
			throw new Exception("Error: The head teacher was not found");

		for (int i = 4; i < splittedBuffer.length; ++i) {
			currentGroup = groupLookup(data, splittedBuffer[i]);
			if (currentGroup == null)
				throw new Exception(
						"Error: A group of the promotion was not found");
			groups.add(currentGroup);
		}

		promo = new Promotion(headTeacher, splittedBuffer[1], groups);
		data.getPromotions().add(promo);
	}

	private Teacher teacherLookup(DataContext data, String lastName,
			String firstName) {
		for (Teacher teacher : data.getTeachers()) {
			if (teacher.getLastName().toUpperCase()
					.equals(lastName.toUpperCase())
					&& teacher.getFirstName().toUpperCase()
							.equals(firstName.toUpperCase())) {
				return teacher;
			}
		}
		return null;
	}

	private void loadRoom(DataContext data, String[] splittedBuffer)
			throws Exception {
		Room room;
		String roomType;
		Building roomBuilding;
		int floor;
		int roomNumber;
		int roomCapacity;

		if (splittedBuffer.length != 6)
			throw new Exception("Error: Invalid room");

		roomBuilding = roomBuildingLookup(data, splittedBuffer[2]);
		if (roomBuilding == null)
			throw new Exception(
					"Error: The room is in a building that doesn't exist");

		roomType = splittedBuffer[1];
		floor = Integer.parseInt(splittedBuffer[3]);
		roomNumber = Integer.parseInt(splittedBuffer[4]);
		roomCapacity = Integer.parseInt(splittedBuffer[5]);
		if (roomType.equals("Amphi")) {
			room = new Amphi(roomBuilding, floor, roomNumber, roomCapacity);
		} else if (roomType.equals("ClassRoom")) {
			room = new ClassRoom(roomBuilding, floor, roomNumber, roomCapacity);
		} else if (roomType.equals("MeetingRoom")) {
			room = new MeetingRoom(roomBuilding, floor, roomNumber,
					roomCapacity);
		} else if (roomType.equals("TDRoom")) {
			room = new TDRoom(roomBuilding, floor, roomNumber, roomCapacity);
		} else if (roomType.equals("TPRoom")) {
			room = new TPRoom(roomBuilding, floor, roomNumber, roomCapacity);
		} else {
			throw new Exception(
					"Error: The type of room specified does not exist");
		}

		data.getRooms().add(room);
	}

	private Building roomBuildingLookup(DataContext data, String buildingName) {
		for (Building building : data.getBuildings()) {
			if (building.getName().toUpperCase()
					.equals(buildingName.toUpperCase())) {
				return building;
			}
		}
		return null;
	}

	private void loadBuilding(DataContext data, String[] splittedBuffer)
			throws Exception {
		Building building;

		if (splittedBuffer.length != 2)
			throw new Exception("Error: Invalid building");
		building = new Building(splittedBuffer[1]);

		data.getBuildings().add(building);
	}

	private void loadGroup(DataContext data, String[] splittedBuffer)
			throws Exception {
		Group group;

		if (splittedBuffer.length != 2)
			throw new Exception("Error: Invalid group");

		group = new Group(splittedBuffer[1]);
		data.getGroups().add(group);
	}

	private void loadStudent(DataContext data, String[] splittedBuffer)
			throws Exception {
		Student student;
		Group studentGroup;

		if (splittedBuffer.length != 4)
			throw new Exception("Error: Invalid student");

		student = new Student(splittedBuffer[2], splittedBuffer[1]);
		studentGroup = groupLookup(data, splittedBuffer[3]);
		if (studentGroup == null)
			throw new Exception(
					"Error: The student is in a group that doesn't exist");

		studentGroup.getStudents().add(student);
	}

	private Group groupLookup(DataContext data, String groupId) {
		for (Group group : data.getGroups()) {
			if (group.getId().toUpperCase().equals(groupId.toUpperCase())) {
				return group;
			}
		}
		return null;
	}

	private void loadTeacher(DataContext data, String[] splittedBuffer)
			throws Exception {
		Teacher teacher;

		if (splittedBuffer.length != 3)
			throw new Exception("Error: Invalid teacher");

		teacher = new Teacher(splittedBuffer[2], splittedBuffer[1]);
		data.getTeachers().add(teacher);

	}
}
